﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using AutoLoL.Entities;
using AutoLoL.Logic.Repositories;

namespace AutoLoL.Logic.Masteries
{
    public class MasteryBuildManager
    {
        private MasteryBuild _masteryBuild;
        private IMasteryRepository _masteryRepository;
        private ISummonerSpellRepository _summonerSpellRepository;

        public MasteryBuildManager(MasteryBuild masteryBuild, IMasteryRepository masteryRepository, ISummonerSpellRepository summonerSpellRepository)
        {
            this._masteryBuild = masteryBuild;
            this._masteryRepository = masteryRepository;
            this._summonerSpellRepository = summonerSpellRepository;
        }

        /// <summary>
        /// Call this method every time something in the build changes
        /// </summary>
        /// <param name="masteryBuild"></param>
        private void Update()
        {
            _masteryBuild.ImprovedSummonerSpells = GetImprovedSummonerSpells();


            foreach (MasteryTree masteryTree in _masteryBuild.MasteryTrees)
            {
                foreach (MasteryValue mastery in masteryTree.MasteryValues)
                {
                    mastery.CanIncreaseValue = CanIncrease(mastery);
                    mastery.CanDecreaseValue = CanDecrease(mastery);
                    mastery.UpdateBindings();
                }

                masteryTree.UpdateBindings();
            }

            _masteryBuild.UpdateBindings();
        }

        public SummonerSpell[] GetImprovedSummonerSpells()
        {
            var summonerSpells = _summonerSpellRepository.GetAll(_masteryRepository);
            return (from summonerSpell in summonerSpells
                    let masteries = _masteryBuild.Masteries.Where(m => m.CurrentRank > 0 && m.MasteryId == summonerSpell.Mastery.MasteryId).FirstOrDefault()
                    where masteries != null
                    select summonerSpell).ToArray();
        }

        private void ResetPoints()
        {
            foreach (MasteryTree masteryTree in _masteryBuild.MasteryTrees)
            {
                foreach (MasteryValue mastery in masteryTree.MasteryValues)
                {
                    mastery.CurrentRank = 0;
                }
            }

            Update();
        }

        public void FromMasteryBuild(MasteryBuild source)
        {
            // We assumue the source is a valid build
            FromString(source.ToString());
        }

        public bool FromString(string masteries)
        {
            if (string.IsNullOrEmpty(masteries))
            {
                return false;
            }

            ResetPoints();

            try
            {
                int index = 0;
                foreach (MasteryTree masteryTree in _masteryBuild.MasteryTrees)
                {
                    foreach (MasteryValue mastery in masteryTree.MasteryValues)
                    {
                        int newRank = int.Parse(masteries[index].ToString());
                        for (int i = 1; i <= newRank; i++)
                        {
                            bool isInreased = TryIncreaseValue(mastery);

                            if (!isInreased)
                            {
                                throw new Exception("Unable to increase mastery value.");
                            }
                        }

                        index++;
                    }
                }

                return true;
            }
            catch
            {
                ResetPoints();
                return false;
            }
        }

        public bool TryDecreaseValue(MasteryValue mastery)
        {
            if (CanDecrease(mastery))
            {
                mastery.CurrentRank--;
                Update();
                return true;
            }

            return false;
        }

        public bool TryIncreaseValue(MasteryValue mastery)
        {
            if (CanIncrease(mastery))
            {
                mastery.CurrentRank++;
                Update();
                return true;
            }

            return false;
        }

        private bool CanDecrease(MasteryValue mastery)
        {
            // Does it have any points to decrease?
            if (mastery.CurrentRank <= 0)
            {
                return false;
            }

            // Is it a prereq for anything? If so, the prereq should not have a value.
            MasteryValue postMastery = mastery.MasteryTree.MasteryValues.Where(c => c.Prerequisite != null && c.Prerequisite.MasteryId == mastery.MasteryId).FirstOrDefault();
            if (postMastery != null)
            {
                if (postMastery.CurrentRank > 0)
                {
                    return false;
                }
            }

            // Will removing a point cause invalid requirements in the rows above?
            foreach (MasteryValue m in mastery.MasteryTree.MasteryValues.Where(c => c.Row > mastery.Row && c.CurrentRank > 0))
            {
                // Calculate totals before this masteries row
                int total = m.MasteryTree.MasteryValues.Where(c => c.Row < m.Row).Sum(c => c.CurrentRank);

                // Can we remove one point?
                if (total == m.PointsToActivate)
                {
                    return false;
                }
            }

            // Decrease is allowed.
            return true;
        }

        private bool CanIncrease(MasteryValue mastery)
        {
            // Are there still points available in the build?
            if (mastery.MasteryTree.MasteryBuild.PointsAvailable <= 0)
            {
                return false;
            }

            // Is it already maxed?
            if (mastery.CurrentRank == mastery.MaximumRank)
            {
                return false;
            }

            // Are there enough points in the previous rows?
            int prevRowPoints = mastery.MasteryTree.MasteryValues.Where(c => c.Row < mastery.Row).Sum(c => c.CurrentRank);
            if (prevRowPoints < mastery.PointsToActivate)
            {
                return false;
            }

            // If there is a prereq, is it full?
            if (mastery.Prerequisite != null)
            {
                MasteryValue prerequisiteValue = mastery.MasteryTree.MasteryValues.Where(c => c.MasteryId == mastery.Prerequisite.MasteryId).Single();
                if (prerequisiteValue.CurrentRank < prerequisiteValue.MaximumRank)
                {
                    return false;
                }
            }

            return true;
        }
    }
}
